<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>代码生成器</title>
	
	<script type="text/javascript" src="jquery.min.js"></script>
	<script type="text/javascript" src="diff_match_patch.min.js"></script>
	
	<style type="text/css">
		*{box-sizing:border-box;}
		body{font-size:14px;line-height:30px;}
		h3{margin:0 0 10px;}
		input,button,select{margin-right:10px;display:inline-block;}
		input[type=text],select{min-width:200px;height:30px;line-height:28px;border:1px #999 solid;border-radius:3px;outline:none 0;background:white;}
		input[type=button],input[type=submit],button{padding:0 10px;height:30px;line-height:28px;border:1px #999 solid;border-radius:3px;outline:none 0;background:#eee;cursor:pointer;}
		input[type=text]:focus,select:focus,input[type=button]:focus,input[type=submit]:focus,button:focus-visible{border-color:#333;}
		input[type=radio],input[type=checkbox]{margin:7px;width:16px;height:16px;border-radius:3px;}
		label{display:inline-block;user-select:none;text-align:right;}
		label.chk{cursor:pointer;}
		label.chk:after{content:'';clear:both;}
		label.chk > *{float:left;}
		input.error,button.error,select.error{border-color:red;}
		div.error{color:red;}
		div.error>b{color:green;}
		div.success{color:green;}
		div.success>b{color:red;}
		div.row{padding:0 0 10px;}
		form label:first-child{width:6em;}
		form.top{text-align:center;padding:10px;border:1px #999 solid;border-bottom:none;}
		form.top>div{display:inline-block;}
		.column2{overflow:hidden;border:1px #999 solid;}
		.column2:after{content:'';clear:both;}
		.column2 > form{margin:0 0 -10000px;padding:10px 10px 10000px;width:50%;}
		.column2 > form.left{float:left;border-right:1px #999 solid;}
		.column2 > form.right{float:right;}
		.diff{display:none;margin:0;padding:5px;border:1px #999 solid;line-height:20px;}
		.diff.normal{white-space:normal;}
		.diff ins{background:green !important;text-decoration:none;color:white;}
		.diff del{background:red !important;text-decoration:none;color:white;}
		.submsg{cursor:pointer;}
		
		.loading {
			display: none;
			position: absolute;
			margin: auto;
			top: 0; bottom: 0; left: 0; right: 0;
			width: 6.250em; height: 6.250em;
			animation: rotate 2.4s linear infinite;
		}
		.loading > .white {
			top: 0; bottom: 0; left: 0; right: 0; 
			background: white; 
			animation: flash 2.4s linear infinite;
			opacity: 0;
		}
		.loading > .dot {
			position: absolute;
			margin: auto;
			width: 2.4em; height: 2.4em;
			border-radius: 100%;
			transition: all 1s ease;
		}
		.loading > .dot:nth-child(2) { top: 0; bottom: 0; left: 0; background: #FF4444; animation: dotsY 2.4s linear infinite; }
		.loading > .dot:nth-child(3) { left: 0; right: 0; top: 0; background: #FFBB33; animation: dotsX 2.4s linear infinite; }
		.loading > .dot:nth-child(4) { top: 0; bottom: 0; right: 0; background: #99CC00; animation: dotsY 2.4s linear infinite; }
		.loading > .dot:nth-child(5) { left: 0; right: 0; bottom: 0; background: #33B5E5; animation: dotsX 2.4s linear infinite; }
		
		@keyframes rotate {
		  0% { transform: rotate( 0 ); }
		  10% { width: 6.250em; height: 6.250em; }
		  66% { width: 2.4em; height: 2.4em; }
		  100%{ transform: rotate(360deg); width: 6.250em; height: 6.250em; }
		}
		
		@keyframes dotsY {
		  66% { opacity: .1; width: 2.4em; }
		  77%{ opacity: 1; width: 0; }
		}
		@keyframes dotsX {
		  66% { opacity: .1; height: 2.4em;}
		  77%{ opacity: 1; height: 0; }
		}
		
		@keyframes flash {
		  33% { opacity: 0; border-radius: 0%; }
		  55%{ opacity: .6; border-radius: 100%; }
		  66%{ opacity: 0; }
		}
	</style>
</head>
<body>
<div id="j-loading" class="loading">
    <div class="dot white"></div>
    <div class="dot"></div>
    <div class="dot"></div>
    <div class="dot"></div>
    <div class="dot"></div>
</div>
<form id="j-save-form" class="top">
	<select id="j-list"></select>
	<input id="j-name" type="text" value="" placeholder="新名" title="新名" />
	<button type="submit">保存历史</button>
	<button type="button">删除历史</button>
	<div id="j-message" class="error"></div>
</form>
<div class="column2">
	<form id="j-model-form" class="left">
		<h3>MySQL表模型</h3>
		<div class="row">
			<label>数据库连接：</label>
			<input id="j-db" type="text" value="db" />
		</div>
		<div class="row">
			<label>表名：</label>
			<select id="j-table"><option value="">请选择</option></select>
			<button id="j-table-btn" type="button">刷新</button>
			<label class="chk"><input id="j-isSplit" type="checkbox" /> 是否把表名中非字母和数字的字符替换为\</label>
		</div>
		<div class="row">
			<label>命名空间：</label>
			<input id="j-namespace" type="text" value="" />
		</div>
		<div class="row">
			<label>模型类名：</label>
			<input id="j-class" type="text" value="" />
		</div>
		<div class="row">
			<label>模型基类：</label>
			<input id="j-base" type="text" value="" />
		</div>
		<div class="row">
			<label></label>
			<label class="chk"><input id="j-isComment" type="checkbox" /> 是否使用字段注释作为标签</label>
		</div>
		<div class="row">
			<label></label>
			<button type="submit">立即生成</button>
			<label class="chk"><input id="j-isOver" type="checkbox" /> 是否覆盖</label>
			<label class="chk"><input id="j-isFiber" type="checkbox" /> 是否Fiber</label>
		</div>
		<div id="j-message"></div>
	</form>
	<form id="j-ctrl-form" class="right">
		<h3>控制器与视图生成</h3>
		<div class="row">
			<label>引用模型：</label>
			<input id="j-model" type="text" value="" />
		</div>
		<div class="row">
			<label>控制器类：</label>
			<input id="j-class" type="text" value="" />
		</div>
		<div class="row">
			<label>控制器基类：</label>
			<input id="j-base" type="text" value="" />
		</div>
		<div class="row">
			<label>搜索模型：</label>
			<input id="j-search" type="text" value="" />
		</div>
		<div class="row">
			<label>视图路径：</label>
			<input id="j-path" type="text" value="" />
			<span>为空时不生成视图且响应结果为JSON数据</span>
		</div>
		<div class="row">
			<label>列表标题：</label>
			<input id="j-title" type="text" value="" />
		</div>
		<div class="row">
			<label></label>
			<button type="submit">立即生成</button>
			<label class="chk"><input id="j-isTpl" type="checkbox" /> 是否使用tpl模板引擎</label>
			<label class="chk"><input id="j-isOver" type="checkbox" /> 是否覆盖</label>
		</div>
		<div id="j-message"></div>
	</form>
</div>
<div id="g-message" class="error"></div>
<div id="j-submsg"></div>
<pre id="j-diff" class="diff"></pre>

<script type="text/javascript">
(function($) {
	const genURL = '/generator/';
	const defstor = {
		// MySQL表模型
		db: 'db',
		table: '',
		namespace: 'app\\models',
		className: '',
		isSplit: false,
		baseClass: 'fwe\\db\\MySQLModel',
		isComment: false,
		isOver: false,
		isFiber: false,
		
		// 控制器与视图生成
		modelClass: '',
		ctrlClass: '',
		ctrlBase: '',
		searchClass: '',
		ctrlPath: '',
		title: '',
		isTpl: false,
	};
	const storage = (function() {
	let json;
		try {
			json = JSON.parse(localStorage.getItem('fwe-generator'));
		} catch(e) {}
		return $.extend({
			// 保存列表
			list: {},
			name: '',
		}, defstor, json);
	})();
	
	window.generatorStorage = storage;
	
	$(document).ajaxStart(function() {
		$('#j-loading').show();
		$('#g-message').hide().empty();
	}).ajaxError(function(evt, xhr, opts, ex) {
		$('#g-message').show().html(ex.message || xhr.responseText || xhr.statusText);
	}).ajaxStop(function() {
		$('#j-loading').hide();
	});
	$.ajaxSetup({global:true});
	
	(function() {
		const $form = $('#j-save-form');
		
		const listElem = $('#j-list', $form).val(storage.db).change(function() {
			const key = $.trim($(this).val());
			if(key !== '') {
				storage.name = key;
				$.extend(storage, defstor, storage.list[key]);
				location.reload();
			} else {
				storage.name = '';
			}
		});
		
		const eachList = function() {
			listElem.empty();
			if($.isEmptyObject(storage.list)) $('<option />').val('').text('另存为').appendTo(listElem);
			$.each(storage.list, function(key) {
				$('<option />').val(key).text(key).attr('selected', key === storage.name).appendTo(listElem);
			});
		};
		
		const nameElem = $('#j-name', $form);
		
		const storKey = function(key) {
			const row = {};
			Object.keys(defstor).forEach(function(key) {
				row[key] = storage[key];
			});
			
			storage.list[key] = row;
		};
		
		$form.submit(function() {
			const $msg = $('#j-message', $form).empty();
			const key = listElem.val();
			const val = $.trim(nameElem.val());
			
			if(key === '') {
				if(val === '') {
					$msg.text('不能为空');
				} else if(val in storage.list) {
					$msg.text('已存在');
				} else {
					storKey(val);
					storage.name = val;
					nameElem.val('');
					eachList();
				}
			} else if(val === '') {
				storKey(key);
			} else if(val in storage.list) {
				$msg.text('已存在');
			} else {
				storKey(val);
				storage.name = val;
				nameElem.val('');
				eachList();
			}
			
			return false;
		});
		
		$('button[type=button]', $form).click(function() {
			const key = listElem.val();
			const val = $.trim(nameElem.val());
			
			delete storage.list[key], storage.list[val];
			storage.name = '';
			eachList();
		});
		
		eachList();
	})();
	
	(function() { // MySQL表模型
		const $form = $('#j-model-form');
		const dbElem = $('#j-db', $form).val(storage.db).change(function() {
			storage.db = $.trim($(this).val());
		});
		const tableElem = $('#j-table', $form).change(function() {
			storage.table = $.trim($(this).val());
			
			const lst = storage.table.split(/[^a-z0-9]+/);
			const lst2 = [];
			$.each(lst, function(i,v) {
				lst2.push(v.charAt(0).toUpperCase() + v.substr(1).toLowerCase());
			});
			storage.className = lst2.join(storage.isSplit ? '\\' : '');
			classElem.val(storage.className);
		});
		const tableBtn = $('#j-table-btn', $form).click(function() {
			$.getJSON(genURL + 'tables', {db: dbElem.val()}, function(list) {
				tableElem.empty();
				$('<option />').val('').text('请选择').appendTo(tableElem);
				$.each(list, function(i, val) {
					$('<option />').val(val).text(val).attr('selected', val === storage.table).appendTo(tableElem);
				});
			});
		}).click();
		const isSplitElem = $('#j-isSplit', $form).change(function() {
			storage.isSplit = $(this).is(':checked');
		}).attr('checked', storage.isSplit);
		const namespaceElem = $('#j-namespace', $form).change(function() {
			storage.namespace = $.trim($(this).val());
		}).val(storage.namespace);
		const classElem = $('#j-class', $form).change(function() {
			storage.className = $.trim($(this).val());
		}).val(storage.className);
		const baseElem = $('#j-base', $form).change(function() {
			storage.baseClass = $.trim($(this).val());
		}).val(storage.baseClass);
		const isCommentElem = $('#j-isComment', $form).change(function() {
			storage.isComment = $(this).is(':checked');
		}).attr('checked', storage.isComment);
		const isOverElem = $('#j-isOver', $form).change(function() {
			storage.isOver = $(this).is(':checked');
		}).attr('checked', storage.isOver);
		const isFiberElem = $('#j-isFiber', $form).change(function() {
			storage.isFiber = $(this).is(':checked');
		}).attr('checked', storage.isFiber);
		$form.submit(function() {
			if(storage.db.length) {
				dbElem.removeClass('error');
			} else {
				dbElem.addClass('error');
			}
			if(storage.table.length) {
				tableElem.removeClass('error');
			} else {
				tableElem.addClass('error');
			}
			if(storage.namespace.length) {
				namespaceElem.removeClass('error');
			} else {
				namespaceElem.addClass('error');
			}
			if(storage.className.length) {
				classElem.removeClass('error');
			} else {
				classElem.addClass('error');
			}
			if(storage.baseClass.length) {
				baseElem.removeClass('error');
			} else {
				baseElem.addClass('error');
			}
			
			$.post(
				genURL + 'model',
				{
					db: storage.db,
					table: storage.table,
					'class': (storage.namespace + '\\' + storage.className),
					base: storage.baseClass,
					isComment: storage.isComment ? 1 : 0,
					isOver: storage.isOver ? 1 : 0,
					isFiber: storage.isFiber ? 1 : 0,
				},
				function(json) {
					const $msg = $('#j-message', $form).empty();
					$('<div class="submsg"/>').appendTo($msg).removeClass('error success').addClass(json.status ? 'success' : 'error').html(json.message).click(function() {
						$('#j-submsg').addClass(json.status ? 'success' : 'error').html(json.message);
						if(json.source && json.target) {
							const dmp = new diff_match_patch();
							dmp.Diff_Timeout = 10;
							dmp.Diff_EditCost = 1;
							
							const d = dmp.diff_main(json.source, json.target);
							
							dmp.diff_cleanupSemantic(d);
							dmp.diff_cleanupEfficiency(d);
							
							$('#j-diff').show().removeClass('normal').html(dmp.diff_prettyHtml(d));
						} else if(json.target) {
							$('#j-diff').show().addClass('normal').html(json.target);
						} else {
							$('#j-diff').hide().removeClass('normal').empty();
						}
					}).click();
				},
				'json'
			);
			
			return false;
		});
	})();
	
	(function() {
		const $form = $('#j-ctrl-form');
		const modelElem = $('#j-model', $form).change(function() {
			storage.modelClass = $.trim($(this).val());
		}).val(storage.modelClass);
		const classElem = $('#j-class', $form).change(function() {
			storage.ctrlClass = $.trim($(this).val());
		}).val(storage.ctrlClass);
		const baseElem = $('#j-base', $form).change(function() {
			storage.ctrlBase = $.trim($(this).val());
		}).val(storage.ctrlBase);
		const searchElem = $('#j-search', $form).change(function() {
			storage.searchClass = $.trim($(this).val());
		}).val(storage.searchClass);
		const pathElem = $('#j-path', $form).change(function() {
			storage.ctrlPath = $.trim($(this).val());
		}).val(storage.ctrlPath);
		const titleElem = $('#j-title', $form).change(function() {
			storage.title = $.trim($(this).val());
		}).val(storage.title);
		const isTplElem = $('#j-isTpl', $form).change(function() {
			storage.isTpl = $(this).is(':checked');
		}).attr('checked', storage.isTpl);
		const isOverElem = $('#j-isOver', $form).change(function() {
			storage.isOver = $(this).is(':checked');
		}).attr('checked', storage.isOver);
		$form.submit(function() {
			if(storage.modelClass.length) {
				modelElem.removeClass('error');
			} else {
				modelElem.addClass('error');
			}
			if(storage.ctrlClass.length) {
				classElem.removeClass('error');
			} else {
				classElem.addClass('error');
			}
			if(storage.ctrlBase.length) {
				baseElem.removeClass('error');
			} else {
				baseElem.addClass('error');
			}
			if(storage.searchClass.length) {
				searchElem.removeClass('error');
			} else {
				searchElem.addClass('error');
			}
			
			$.post(
				genURL + 'ctrl',
				{
					model: storage.modelClass,
					'class': storage.ctrlClass,
					base: storage.ctrlBase,
					search: storage.searchClass,
					path: storage.ctrlPath,
					title: storage.title,
					isTpl: storage.isTpl ? 1 : 0,
					isOver: storage.isOver ? 1 : 0,
				},
				function(list) {
					const $msg = $('#j-message', $form).empty();
					list.forEach(function(json) {
						$('<div class="submsg"/>').appendTo($msg).removeClass('error success').addClass(json.status ? 'success' : 'error').html(json.message).click(function() {
							$('#j-submsg').addClass(json.status ? 'success' : 'error').html(json.message);
							if(json.source && json.target) {
								const dmp = new diff_match_patch();
								dmp.Diff_Timeout = 10;
								dmp.Diff_EditCost = 1;
								
								const d = dmp.diff_main(json.source, json.target);
								
								dmp.diff_cleanupSemantic(d);
								dmp.diff_cleanupEfficiency(d);
								
								$('#j-diff').show().removeClass('normal').html(dmp.diff_prettyHtml(d));
							} else if(json.target) {
								$('#j-diff').show().addClass('normal').html(json.target);
							} else {
								$('#j-diff').hide().removeClass('normal').empty();
							}
						});
					});
					$msg.children().first().click();
				},
				'json'
			);
			
			return false;
		});
	})();

	$(window).unload(function() {
		localStorage.setItem('fwe-generator', JSON.stringify(storage));
	});
})(jQuery);
</script>
</body>
</html>
